summaryrefslogtreecommitdiff
path: root/app/[lng]
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-25 22:04:56 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-25 22:04:56 +0900
commit2b59582194fc5c23140f52c42c793c324856a35e (patch)
tree0db8ef0e913b3a44dfd6e3e20fe92b8e4984aeba /app/[lng]
parent835df8ddc115ffa74414db2a4fab7efc0d0056a9 (diff)
(김준회) 벤더풀&AVL 구매 추가요청사항 반영
Diffstat (limited to 'app/[lng]')
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx17
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx193
2 files changed, 55 insertions, 155 deletions
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx
index b065919f..c0230013 100644
--- a/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx
+++ b/app/[lng]/evcp/(evcp)/(procurement)/avl/[id]/page.tsx
@@ -4,9 +4,9 @@ import { notFound } from "next/navigation"
import { getValidFilters } from "@/lib/data-table"
import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { getAvlLists, getAvlDetail } from "@/lib/avl/service"
+import { getAllAvlDetail } from "@/lib/avl/service"
import { avlDetailSearchParamsCache } from "@/lib/avl/validations"
-import { AvlDetailTable } from "@/lib/avl/table/avl-detail-table"
+import { AvlDetailVirtualTable } from "@/lib/avl/table/avl-detail-virtual-table"
import { getAvlListById } from "@/lib/avl/service"
import { getAllProjectInfoByProjectCode as getProjectInfoFromBiddingProjects } from "@/lib/bidding-projects/service"
import { getAllProjectInfoByProjectCode as getProjectInfoFromProjects } from "@/lib/projects/service"
@@ -36,21 +36,17 @@ export default async function AvlDetailPage(props: AvlDetailPageProps) {
}
// 프로젝트 테이블 먼저
- let projectInfo = await getProjectInfoFromProjects(avlListInfo.projectCode || '')
+ let projectInfo: any = await getProjectInfoFromProjects(avlListInfo.projectCode || '')
// 없으면 견적프로젝트 테이블 조회
if (!projectInfo) {
projectInfo = await getProjectInfoFromBiddingProjects(avlListInfo.projectCode || '')
}
// 배열로 오니 첫번째것만
- projectInfo = projectInfo[0]
+ projectInfo = projectInfo[0] as any
const promises = Promise.all([
- getAvlDetail({
- ...search,
- filters: validFilters,
- avlListId: numericId,
- }),
+ getAllAvlDetail(numericId),
])
return (
@@ -108,9 +104,8 @@ function AvlDetailTableWrapper({
const shipOwnerName = avlListInfo.shipOwnerName || undefined
return (
- <AvlDetailTable
+ <AvlDetailVirtualTable
data={data}
- pageCount={pageCount}
avlListId={avlListId}
avlType={avlType}
projectInfo={projectInfo}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx
index 7426e069..f18716a3 100644
--- a/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx
+++ b/app/[lng]/evcp/(evcp)/(procurement)/vendor-pool/page.tsx
@@ -1,25 +1,52 @@
"use client"
import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
import { Shell } from "@/components/shell"
-import { getVendorPools } from "@/lib/vendor-pool/service"
-import { vendorPoolSearchParamsCache } from "@/lib/vendor-pool/validations"
-import { VendorPoolTable } from "@/lib/vendor-pool/table/vendor-pool-table"
import { InformationButton } from "@/components/information/information-button"
-import { useSearchParams } from "next/navigation"
+import { VendorPoolVirtualTable } from "@/lib/vendor-pool/table/vendor-pool-virtual-table"
+import { Skeleton } from "@/components/ui/skeleton"
+import type { VendorPoolItem } from "@/lib/vendor-pool/table/vendor-pool-table-columns"
+import { toast } from "sonner"
-interface VendorPoolPageProps {
- searchParams: Promise<SearchParams>
-}
+export default function VendorPoolPage() {
+ const [data, setData] = React.useState<VendorPoolItem[]>([])
+ const [isLoading, setIsLoading] = React.useState(true)
+
+ // 전체 데이터 로드
+ const loadData = React.useCallback(async () => {
+ setIsLoading(true)
+ try {
+ const response = await fetch('/api/vendor-pool/all')
+ if (!response.ok) {
+ throw new Error('Failed to fetch data')
+ }
+ const result = await response.json()
+ setData(result)
+ } catch (error) {
+ console.error('Failed to load vendor pool data:', error)
+ toast.error('데이터를 불러오는데 실패했습니다.')
+ } finally {
+ setIsLoading(false)
+ }
+ }, [])
+
+ // 초기 로드
+ React.useEffect(() => {
+ loadData()
+ }, []) // ✅ 빈 배열로 변경 - 마운트시에만 실행
+
+ // 새로고침 핸들러 - useRef를 사용하여 안정적인 참조 유지
+ const loadDataRef = React.useRef(loadData)
+ React.useEffect(() => {
+ loadDataRef.current = loadData
+ }, [loadData])
+
+ const handleRefresh = React.useCallback(() => {
+ loadDataRef.current()
+ }, []) // ✅ 빈 배열로 변경 - 함수 재생성 방지
-export default function VendorPoolPage({ searchParams }: VendorPoolPageProps) {
return (
- <Shell className="gap-2">
+ <Shell variant="fullscreen" className="gap-2 h-[calc(100vh-150px)]">
<div className="flex items-center justify-between space-y-2">
<div className="flex items-center justify-between space-y-2">
<div>
@@ -33,136 +60,14 @@ export default function VendorPoolPage({ searchParams }: VendorPoolPageProps) {
</div>
</div>
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={30}
- searchableColumnCount={1}
- filterableColumnCount={5}
- cellWidths={[
- "50px", "60px", "100px", "80px", "120px", "120px", "120px", "120px",
- "120px", "100px", "140px", "140px", "130px", "120px", "80px", "100px",
- "120px", "80px", "100px", "120px", "100px", "110px", "140px", "130px",
- "60px", "100px", "80px", "120px", "80px", "80px"
- ]}
- shrinkZero
- />
- }
- >
- <VendorPoolTableWrapperClient searchParamsPromise={searchParams} />
- </React.Suspense>
+ {isLoading ? (
+ <div className="space-y-4 flex-1 flex flex-col">
+ <Skeleton className="h-10 w-full" />
+ <Skeleton className="h-full w-full flex-1" />
+ </div>
+ ) : (
+ <VendorPoolVirtualTable data={data} onRefresh={handleRefresh} />
+ )}
</Shell>
)
}
-
-// 클라이언트 컴포넌트: 필터 변경을 감시하여 데이터 재조회
-function VendorPoolTableWrapperClient({ searchParamsPromise }: { searchParamsPromise: Promise<SearchParams> }) {
- const searchParams = useSearchParams()
- const [initialData, setInitialData] = React.useState<{ data: any[], pageCount: number } | null>(null)
- const [data, setData] = React.useState<any[]>([])
- const [pageCount, setPageCount] = React.useState(0)
- const [isLoading, setIsLoading] = React.useState(false)
-
- // 초기 데이터 로딩
- React.useEffect(() => {
- const loadInitialData = async () => {
- try {
- const searchParamsData = await searchParamsPromise
- const search = vendorPoolSearchParamsCache.parse(searchParamsData)
- const validFilters = getValidFilters(search.filters)
-
- const result = await getVendorPools({
- ...search,
- filters: validFilters,
- })
-
- setInitialData(result)
- setData(result.data)
- setPageCount(result.pageCount)
- } catch (error) {
- console.error('Failed to load initial data:', error)
- }
- }
- loadInitialData()
- }, [searchParamsPromise])
-
- // 필터 상태 변경 감시 및 데이터 재조회
- React.useEffect(() => {
- if (!initialData) return // 초기 데이터가 로드되기 전까지는 실행하지 않음
-
- const refreshData = async () => {
- setIsLoading(true)
- try {
- const currentParams = Object.fromEntries(searchParams.entries())
- const search = vendorPoolSearchParamsCache.parse(currentParams)
- const validFilters = getValidFilters(search.filters)
-
- const result = await getVendorPools({
- ...search,
- filters: validFilters,
- })
-
- setData(result.data)
- setPageCount(result.pageCount)
- } catch (error) {
- console.error('Failed to refresh vendor pool data:', error)
- } finally {
- setIsLoading(false)
- }
- }
-
- // 필터 파라미터가 변경될 때마다 데이터 재조회
- const currentFilters = searchParams.get('filters')
- const currentJoinOperator = searchParams.get('joinOperator')
- const currentSearch = searchParams.get('search')
- const currentPage = searchParams.get('page')
- const currentPerPage = searchParams.get('perPage')
- const currentSort = searchParams.get('sort')
-
- // 필터 관련 파라미터가 변경되면 재조회
- if (currentFilters !== '[]' || currentJoinOperator || currentSearch || currentPage || currentPerPage || currentSort) {
- refreshData()
- } else {
- // 필터가 초기 상태면 초기 데이터로 복원
- setData(initialData.data)
- setPageCount(initialData.pageCount)
- }
- }, [searchParams, initialData])
-
- const handleRefresh = React.useCallback(async () => {
- if (!initialData) return
-
- setIsLoading(true)
- try {
- const currentParams = Object.fromEntries(searchParams.entries())
- const search = vendorPoolSearchParamsCache.parse(currentParams)
- const validFilters = getValidFilters(search.filters)
-
- const result = await getVendorPools({
- ...search,
- filters: validFilters,
- })
-
- setData(result.data)
- setPageCount(result.pageCount)
- } catch (error) {
- console.error('Failed to refresh vendor pool data:', error)
- } finally {
- setIsLoading(false)
- }
- }, [searchParams, initialData])
-
- if (!initialData) {
- return null // 초기 데이터 로딩 중
- }
-
- return (
- <VendorPoolTable
- data={data}
- pageCount={pageCount}
- onRefresh={handleRefresh}
- />
- )
-}